package com.leyou.item.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.leyou.common.pojo.PageResult;
import com.leyou.item.bo.SpuBo;
import com.leyou.item.mapper.*;
import com.leyou.item.pojo.*;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Transactional
public class GoodsService {

    // 查询商品的spu
    @Autowired
    private SpuMapper spuMapper;

    // 查询类目
    @Autowired
    private CategoryService categoryService;

    // 查询品牌
    @Autowired
    private BrandMapper brandMapper;

    // 商品详情
    @Autowired
    private SpuDetailMapper spuDetailMapper;

    // 商品的sku
    @Autowired
    private SkuMapper skuMapper;

    // 商品库存
    @Autowired
    private SkuStock skuStock;

    // 发送消息的AmqpTemplate模板
    @Autowired
    private AmqpTemplate amqpTemplate;

    private static final Logger LOGGER = LoggerFactory.getLogger(GoodsService.class);


    /**
     * 查询商品
     *
     * @param key
     * @param saleable
     * @param page
     * @param rows
     * @return
     */
    public PageResult<SpuBo> querySpuBoByPage(String key, Boolean saleable, Integer page, Integer rows) {
        // 初始化example对象
        Example example = new Example(Spu.class);
        Example.Criteria criteria = example.createCriteria();
        // 添加搜索条件
        // 搜索的关键字
        if (StringUtils.isNotBlank(key)) {
            criteria.andLike("title", "%" + key + "%");
        }
        // 是否上下架
        if (saleable != null) {
            criteria.andEqualTo("saleable", saleable);
        }
        // 分页条件，后面要紧跟查询方法
        PageHelper.startPage(page, rows);

        // 执行查询,把结果封装到分页里
        List<Spu> spus = this.spuMapper.selectByExample(example);
        PageInfo<Spu> pageInfo = new PageInfo<>(spus);

        // 查询大类和品牌
        // 创建spuBo对象，把数据都搞进去。
        //List<SpuBo> list = new ArrayList<>();
        //spus.forEach(spu -> {
        //    SpuBo spuBo = new SpuBo();
        //    // copy共同属性的值到新的对象，使用工具类BeanUtils把spu对象下的属性，拷贝到spuBo对象下。
        //    BeanUtils.copyProperties(spu, spuBo);
        //    // 把spuBo下的cname类目和bname品牌信息查询出来赋值。
        //    List<String> names = this.categoryService.queryNameByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
        //    // 把cname设置到spuBo对象里，使用"/"划分
        //    spuBo.setCname(StringUtils.join(names, "/"));
        //
        //    // 查询品牌,设置到cname
        //    Brand brand = this.brandMapper.selectByPrimaryKey(spu.getBrandId());
        //    spuBo.setBname(brand.getName());
        //    // 封装好的对象放到spuBos数组里
        //    spuBos.add(spuBo);
        //});

        // 使用stream流的方式处理数据。使用map函数处理每个对象数据,返回的数据封装成数组spuBos
        List<SpuBo> spuBos = spus.stream().map(spu -> {
            SpuBo spuBo = new SpuBo();
            // copy共同属性的值到新的对象，使用工具类BeanUtils把spu对象下的属性，拷贝到spuBo对象下。
            BeanUtils.copyProperties(spu, spuBo);
            // 把spuBo下的cname类目和bname品牌信息查询出来赋值。
            List<String> names = this.categoryService.queryNameByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
            // 把cname设置到spuBo对象里，使用"/"划分
            spuBo.setCname(StringUtils.join(names, "/"));

            // 查询品牌,设置到cname
            Brand brand = this.brandMapper.selectByPrimaryKey(spu.getBrandId());
            spuBo.setBname(brand.getName());
            // 返回
            return spuBo;
        }).collect(Collectors.toList());

        // 返回分页结果集，总页码和结果集
        return new PageResult<>(pageInfo.getTotal(), spuBos);
    }


    /**
     * 保存商品的方法
     *
     * @param spuBo
     */
    public void saveGoods(SpuBo spuBo) {
        // 新增的时候设置下没有传参数属性的默认属性
        spuBo.setId(null);
        spuBo.setSaleable(true); // 是否上架
        spuBo.setValid(true);   // 是否有效
        spuBo.setCreateTime(new Date());
        spuBo.setLastUpdateTime(spuBo.getCreateTime()); // 去创建日期的日期。
        // 插入到spu表里，由于tb_spu表设置ID是自增长的id，这个insert动作一执行，默认id也就是spu的id主键就有了（mybatis实现的）。
        this.spuMapper.insertSelective(spuBo);

        // 插入到spuDetail表
        SpuDetail spuDetail = spuBo.getSpuDetail();
        spuDetail.setSpuId(spuBo.getId());
        this.spuDetailMapper.insertSelective(spuDetail);

        // 插入sku信息表
        //List<Sku> skus = spuBo.getSkus();
        // 把下面的插入抽离出一个方法。下面修改需要用到
        this.saveSkuAndStock(spuBo);
        //skus.forEach(sku -> {
        //    sku.setSpuId(spuBo.getId());
        //    sku.setCreateTime(new Date());
        //    sku.setLastUpdateTime(sku.getCreateTime());
        //    this.skuMapper.insertSelective(sku);
        //
        //    // 插入库存stock表
        //    Stock stock = new Stock();
        //    stock.setSkuId(sku.getId());
        //    stock.setStock(sku.getStock());
        //    this.skuStock.insertSelective(stock);
        //});

        // 发送到队列里插入的消息，传spuid
        sendMessage("insert",spuBo.getId());
    }


    /**
     * 根据商品spuid查询detail的方法
     *
     * @param pid
     * @return
     */
    public SpuDetail querySpuDetailByPid(Long pid) {
        return this.spuDetailMapper.selectByPrimaryKey(pid);
    }

    /**
     * 根据商品spuid查询sku的方法
     *
     * @param pid
     * @return
     */
    public List<Sku> querySkuByPid(Long pid) {
        Sku sku = new Sku();
        sku.setSpuId(pid);
        // 为了方便显示顺便把库存一起查出来
        List<Sku> skus = this.skuMapper.select(sku);
        skus.forEach(s -> {
            // 根据每个sku的id查出来stock对象。
            Stock stock = this.skuStock.selectByPrimaryKey(s.getId());
            // 把每个sku里设置查出来的库存值
            s.setStock(stock.getStock());
        });

        return skus;
    }

    /**
     * 修改商品：修改商品的话spu可以改，但是sku的话只能删除。因为有可能之前存的sku属性都不存在。没法都update
     * 1、删除stock库存  2、删除sku  3、新增stock和sku  4、更新spu和spuDetail
     *
     * @param spuBo
     */
    public void editGoods(SpuBo spuBo) {
        // 使用之前的查skus方法查询
        List<Sku> skus = this.querySkuByPid(spuBo.getId());
        // 如果skus存在删掉
        if (!CollectionUtils.isEmpty(skus)) {
            // 获取sku的id数组
            List<Long> skuIds = skus.stream().map(Sku::getId).collect(Collectors.toList());

            // 删除以前的库存,因为默认方法里没有in，只能用模板使用adnIn方式删除
            Example example = new Example(Stock.class);
            example.createCriteria().andIn("skuId", skuIds);
            this.skuStock.deleteByExample(example);

            // 删除sku的数据，根据sku里的spu_id
            Sku sku = new Sku();
            // 设置sku的SpuId,删除spu_id等的值。。
            sku.setSpuId(spuBo.getId());
            this.skuMapper.delete(sku);
        }

        // 不存在的话执行插入sku和stock表（新增sku和库存）
        saveSkuAndStock(spuBo);

        // 更新spu,把不需要更新的值设为null
        spuBo.setLastUpdateTime(new Date());
        spuBo.setCreateTime(null);
        spuBo.setValid(null);
        spuBo.setSaleable(null);
        this.spuMapper.updateByPrimaryKeySelective(spuBo);

        // 更新spu详情
        this.spuDetailMapper.updateByPrimaryKeySelective(spuBo.getSpuDetail());

        // 新增更改商品发送消息的方法
        sendMessage("update",spuBo.getId());

    }

    // 把插入sku和skuDetail抽离出一个方法
    public void saveSkuAndStock(SpuBo spuBo) {
        // 不存在的话执行插入（新增sku和库存）
        spuBo.getSkus().forEach(sku -> {
            sku.setSpuId(spuBo.getId());
            sku.setCreateTime(new Date());
            sku.setLastUpdateTime(sku.getCreateTime());
            //插入sku
            this.skuMapper.insertSelective(sku);

            // 插入库存stock表
            Stock stock = new Stock();
            stock.setSkuId(sku.getId());
            stock.setStock(sku.getStock());
            this.skuStock.insertSelective(stock);
        });
    }


    /**
     * 要根据pid删4张表 spu\spuDetail\sku\stock
     *
     * @param pid
     */
    public void delGoods(Long pid) {
        // 使用之前根据pid查skus的方法查询sku的所有id
        List<Sku> skus = this.querySkuByPid(pid);
        List<Long> ids = skus.stream().map(Sku::getId).collect(Collectors.toList());

        // 根据id去删除库存，默认方法里没有in，只能用模板使用adnIn方式删除
        Example example = new Example(Stock.class);
        example.createCriteria().andIn("skuId", ids);
        this.skuStock.deleteByExample(example);

        // 删除sku的数据，根据sku里的spu_id
        Sku sku = new Sku();
        sku.setSpuId(pid);
        this.skuMapper.delete(sku);

        // 删除spuDetail的数据
        this.spuDetailMapper.deleteByPrimaryKey(pid);
        this.spuMapper.deleteByPrimaryKey(pid);

        // 删除商品的消息
        sendMessage("delete",pid);
    }

    // 上架
    public void upSpu(Long pid) {
        this.spuMapper.upSpu(pid);
    }

    // 下架
    public void downSpu(Long pid) {
        this.spuMapper.downSpu(pid);
    }

    // 根据spu的ID查询spu
    public Spu querySpuById(Long id) {
        return this.spuMapper.selectByPrimaryKey(id);
    }


    // rabbitMQ发送消息的方法抽离出来
    public void sendMessage(String type, Long spuID) {
        try {
            // 发送消息item.insert 或者 item.delete 封装成参数
            amqpTemplate.convertAndSend("item." + type, spuID);
        } catch (Exception e) {
            // 定义发送异常发送的日志
            LOGGER.error("{}商品消息发送异常，商品id:{}", type, spuID, e);
        }

    }

    // 根据id查询sku的信息
    public Sku querySkuById(Long id) {
        return this.skuMapper.selectByPrimaryKey(id);
    }
}
